Пульт.Онлайн /

Пример использования API

Пример организации работы с API сервера Пульт.Онлайн по протоколу JSONRPC2.0 через Websocket. Пример состоит из четырех коротких файлов (html+js+css) и не требует подключения сторонних библиотек или фреймворков.

В примере реализован минималистичный веб-сайт, отображающий список узлов одного из проектов Демо-сервера. Для каждого узла выполняется мониторинг нескольких переменных и вывод их значений на страницу, с возможностью изменять значения уставок.

Рабочий пример


Исходные файлы

  • index.html

      <!DOCTYPE html>
      <html lang="ru">
          <head>
              <meta charset="UTF-8">
              <meta name="viewport" content="width=device-width, initial-scale=1.0">
              <title>Данные с сервера Пульт.Онлайн</title>
              <link rel="stylesheet" href="style.css">
              <script src="jsonrpc.js"></script>
              <script src="script.js"></script>
          </head>
          <body>
              <div class="container">
                  <h3>Данные с сервера Пульт.Онлайн</h3>
                  <div id="data"></div>
              </div>
          </body>
      </html>		
    
  • script.js (общая логика)

      const url='wss://webscada.ru';
      const apikey='31269DCE4F983A31FBE8B88DF67CDAE2';
      const project='Приточная вентиляция';
      const vars=['temp_setpoint','temp_outdoor','temp_room'];
    
      let rpc=null;
      let pingInterval=null;
      let timeout=5000;
    
      document.addEventListener('DOMContentLoaded',function(e){
          rpc = new JsonRpc(url,timeout,onConnected,onNotification);
      });
    
      function onConnected(){
          
          // Организуем постоянный пинг сервера, чтобы предотвратить отключение по неактивности
          if(!pingInterval){
              pingInterval=setInterval(function (){
                  rpc.call("system_ping",null,function(result, error){
                      console.log('pong');
                  });
              },timeout);
          }
    
          // Получаем список всех узлов в проекте
    
          let method='fdb_list';
          let params={
              pult_apikey:apikey,
              path:`/projects/${project}.project/nodes`,
          }
    
          rpc.call(method,params, function(result, error){
              if (error) {
                  console.error(method,'Error:', error);
              }else{
                  console.log(method,'Result:', result);
    
                  // Генерируем список глобальных имен переменных
                  
                  let html=[];
                  let global_vars=[];
    
                  for(let i=0; i<result.length; i++){
                      let n=result[i];
                      html.push(`<h4>${n.name}</h4>`);
                      for(let i=0; i<vars.length; i++){
                          let v=vars[i];
                          let global_name=`${n.prefix}_${v}`;
                          global_vars.push(global_name);
                          if(v==='temp_setpoint'){ // выводим поле ввода
                              html.push(`<div><span>${v}</span><input id='${global_name}' value='[-loading-]'/></div>`);
                          }else{ // выводим простой нередактируемый текст
                              html.push(`<div><span>${v}</span><span id='${global_name}'>[-loading-]</span></div>`);
                          }
                      }
                  }
                  
                  // Создаем html-элементы для отображения информации
                  let data=document.getElementById('data');
                  data.innerHTML=html.join('');
    
                  // Привязывем обработчик к полям ввода
                  let inputs=data.querySelectorAll('input');
                  for(let i=0; i<inputs.length; i++){
                      let input=inputs[i];
                      input.onchange=function (e){
                          console.log('change',input.id,input.value);
                          rpc.call('var_set',{
                              pult_apikey:apikey,
                              var:input.id,
                              value:input.value
                          }, function(result, error){
                              console.log(result,error);
                              if(error){
                                  input.classList.add('error');
                                  setTimeout(function (){  // Развязываем алерт через таймаут, чтобы не блокировать отрисовку ошибки
                                      alert(error.message); // Выводим ошибку
                                  },100);
                              }else{
                                  input.classList.remove('error');
                                  input.classList.add('success');
                                  setTimeout(function (){ // Сбрасываем класс после короткой индикации
                                      input.classList.remove('success');
                                  },100);
                              }
                          });
    
                      }
                  }
    
                  // Отправляем запрос на подписку на глобальные переменные
    
                  let params={
                      pult_apikey:apikey,
                      event:`var_update`,
                      options:{
                          'vars':global_vars,
                      }
                  }
    
                  rpc.call('subscribe',params,function (result,error){
                      if (error) {
                          console.error(method,'Error:', error);
                      }else{
                          console.log(result);
                      }
                  });
    
              }
          });
      }
    
      function onNotification(method,params){
          console.log(method,params);
          for(let i=0; i<params.data.changes.length; i++){
              let [global_name,dt,value,error,payload]=params.data.changes[i];
              let element=document.getElementById(global_name);
              if(element.tagName==='INPUT'){
                  element.value=value;
              }else{
                  element.innerHTML=value;
              }
          }
      }
    
  • jsonrpc.js (библиотека для работы с JSONRPC2.0/WS)

      class JsonRpc{
          constructor(url,timeout,onConnected,onNotification) {
              this.url = url;
              this.onConnected=onConnected;
              this.onNotification=onNotification;
              this.socket = null;
              this.callbacks = {};
              this.requestId = 1;
              this.timeout=timeout;
              this.connect();
          }
      
          connect() {
              this.socket = new WebSocket(this.url);
          
              this.socket.onopen = () => {
                  console.log('WebSocket connected');
                  this.onConnected();
              };
          
              this.socket.onclose = () => {
                  console.log('WebSocket disconnected');
                  // Попытка переподключения через 5 секунд
                  setTimeout(() => this.connect(), this.timeout);
              };
          
              this.socket.onerror = (error) => {
                  console.error('WebSocket error:', error);
              };
          
              this.socket.onmessage = (event) => {
                  
                  try {
                      const message = JSON.parse(event.data);
                  
                      if(message.id===undefined){ // JSONRPC2.0 notification
    
                          this.onNotification(message.method,message.params);
    
                      }else{ // JSONRPC2.0 message
                          // Проверяем, что это ответ на наш запрос
                          if (message.id && this.callbacks[message.id]) {
                              const callback = this.callbacks[message.id];
                              delete this.callbacks[message.id];
                          
                              // Вызываем коллбэк с данными ответа
                              callback(message.result || null, message.error || null);
                          }
                      }
    
                  } catch (e) {
                      console.error('Error parsing JSON-RPC message:', e);
                  }
              };
          }
      
          call(method, params, callback) {
              if (this.socket.readyState !== WebSocket.OPEN) {
                  console.error('WebSocket is not open');
                  callback(null, { code: -1, message: 'WebSocket is not open' });
                  return;
              }
      
              const id = this.requestId++;
              const request = {
                  jsonrpc: "2.0",
                  method: method,
                  params: params,
                  id: id
              };
          
              this.callbacks[id] = callback;
              this.socket.send(JSON.stringify(request));
          }
      }
    
  • style.css

      .container{
          background-color: #f0f0f0;
          padding: 20px;
      }
    
      .container h4{
          margin-top: 20px;
          margin-bottom: 10px;
      }
    
      .error{
          background-color: red;
          color: white;
      }
    
      .success{
          background-color: rgb(120, 245, 120);
      }
    
      #data > div{
          display: table-row;
      }
    
      #data > div > *{
          display: table-cell;
          padding: 5px 20px 5px 5px;
      }
    

Как использовать

  • Сохраните каждый файл с соответствующим именем в одной папке.
  • Откройте index.html в браузере.
  Email
  SMS
   fdb_list
   fdb_load
   fdb_move
   fdb_paths
   node_list
   subscribe
   var_get
   var_list
   var_set